---
title: "Tools"
description: "Building and configuring MCP tools"
icon: "wrench"
---

Tools are the primary way MCP clients interact with your server. They represent functions that can be invoked with parameters and return results. This guide covers everything you need to know about creating powerful and reliable tools.

<Tip>
**Response Helpers**: Throughout this guide, you'll see code examples using response helpers like `text()`, `object()`, and `mix()`. These utilities simplify creating tool responses with proper typing and metadata. See [Response Helpers](./response-helpers) for a complete reference.
</Tip>

## Understanding Tools

Tools in MCP are:
- **Invocable Functions**: Clients can call them with parameters
- **Typed**: Parameters and returns have defined types
- **Async**: All tool handlers can be asynchronous

## Basic Tool Structure

Every tool has three main components:

```typescript
import { z } from 'zod';

server.tool({
  name: 'tool_name',           // Unique identifier
  description: 'What it does',  // Clear description for clients
  schema: z.object({...}),      // Zod schema for input validation
}, async (params) => {...})    // Handler function
```

## Input Validation with Zod

Tools use [Zod](https://zod.dev/) schemas for input validation. The server automatically validates inputs before calling your handler, so you can trust that the parameters match your schema.

### Basic Input Types

```typescript
import { z } from 'zod';

server.tool({
  name: 'process_message',
  description: 'Process a message with various options',
  schema: z.object({
    // Required string
    message: z.string().describe('The message to process'),
    
    // Optional number with default
    count: z.number().default(10).describe('Number of items'),
    
    // Optional boolean with default
    verbose: z.boolean().default(false).describe('Enable verbose output'),
    
    // Optional object
    config: z.object({
      setting1: z.string(),
      setting2: z.number()
    }).optional().describe('Configuration object'),
    
    // Array of strings
    items: z.array(z.string()).describe('List of items to process')
  })
}, async ({ message, count, verbose, config, items }) => {
  // message, count, verbose, config, items are fully typed and validated
  // count will be 10 if not provided
  // verbose will be false if not provided
  return text('Results...')
})
```


## Tool Callbacks

### Basic Response

The simplest tool response is text. Use the `text()` helper for clean, readable code:

<CodeGroup>

```typescript Helper
import { text } from 'mcp-use/server';

async (params) => {
  return text('Response text here');
}
```

```typescript Expanded
async (params) => {
  return {
    content: [{
      type: 'text',
      text: 'Response text here'
    }],
    _meta: {
      mimeType: 'text/plain'
    }
  }
}
```

</CodeGroup>

### Multiple Content Items

Tools can return multiple content items using the `mix()` helper:

<CodeGroup>

```typescript Helper (Recommended)
import { mix, text, resource, object } from 'mcp-use/server';

cb: async ({ data }) => {
  return mix(
    text('Analysis complete:'),
    text(`Found ${data.length} items`),
    resource('results://latest', object({ items: data }))
  );
}
```

```typescript Expanded
cb: async ({ data }) => {
  return {
    content: [
      {
        type: 'text',
        text: 'Analysis complete:'
      },
      {
        type: 'text',
        text: `Found ${data.length} items`
      },
      {
        type: 'resource',
        resource: {
          uri: 'results://latest',
          mimeType: 'application/json',
          text: JSON.stringify({ items: data }, null, 2)
        }
      }
    ]
  }
}
```

</CodeGroup>



## Tool Annotations

Add metadata to tools for better client integration:

```typescript
server.tool({
  name: 'important_operation',
  description: 'Performs an important operation',
  schema: z.object({ ... }),
  annotations: {
    requiresAuth: true,
    rateLimit: '10/minute',
    deprecated: false
  }
}, async (params) => {
  // Tool implementation
})
```

## Returning Widgets from Tools

Tools can return interactive UI widgets using the `widget()` helper and the `returnsWidget` option. This automatically configures the tool to render widgets in the Inspector and ChatGPT.

<Warning>
 You must set the `returnsWidget` option to `true` in the tool definition to return a widget.
</Warning>

```typescript
import { widget } from 'mcp-use/server';
import { z } from 'zod';

server.tool({
  name: 'get-weather',
  description: 'Get current weather for a city',
  schema: z.object({
    city: z.string().describe('City name')
  }),
  // Widget config sets all registration-time metadata
  widget: {
    name: 'weather-display',  // Must match a widget in resources/
    invoking: 'Fetching weather...',
    invoked: 'Weather loaded'
  }
}, async ({ city }) => {
  // Fetch weather data
  const weatherData = await fetchWeather(city);
  
  // Return widget with runtime data only
  return widget({
    data: weatherData,
    message: `Current weather in ${city}`
  });
});
```

**How it works:**
- `widget: { name, invoking, invoked, ... }` - Configures all widget metadata at registration time
- `widget()` helper - Returns runtime data (structured content and optional message)
- The widget must exist in your `resources/` folder and be registered with the server

See [UI Widgets](./ui-widgets) for complete widget creation and registration documentation.

## OpenAI Apps SDK Integration

For ChatGPT and OpenAI compatible clients:

<Tip>
**Recommended Approach**: The recommended way to create OpenAI Apps SDK widgets is to use the [widget helper with returnsWidget option](./ui-widgets#custom-tools-with-widgets) or [automatic widget mounting](./ui-widgets#automatic-widget-registration), which automatically registers widgets from the `resources/` folder as tools. The manual approach below is shown for reference.
</Tip>

```typescript
server.tool({
  name: 'show_chart',
  description: 'Display a chart',
  schema: z.object({
    data: z.array(z.any()).describe('The chart data')
  }),
  _meta: {
    'openai/outputTemplate': 'ui://widgets/chart',
    'openai/toolInvocation/invoking': 'Generating chart...',
    'openai/toolInvocation/invoked': 'Chart generated',
    'openai/widgetAccessible': true
  }
}, async ({ data }) => {
  return {
    _meta: {
      'openai/outputTemplate': 'ui://widgets/chart'
    },
    content: [{
      type: 'text',
      text: 'Chart displayed'
    }],
    structuredContent: { data }
  }
})
```

## Error Handling with error()

The `error()` helper provides a standardized way to return error responses from tools. It sets the `isError` flag to `true`, allowing clients to distinguish between successful results and error conditions. This ensures consistent error handling across your MCP server.

The `error()` helper creates a properly formatted error response with:
- `isError: true` flag to indicate failure
- Text content with your error message
- Proper MIME type metadata


<CodeGroup>

```typescript Helper (Recommended)
import { object, error } from 'mcp-use/server';

server.tool({
  name: 'external_api',
  description: 'Call external API',
  schema: z.object({
    endpoint: z.string().url().describe('The API endpoint URL')
  })
}, async ({ endpoint }) => {
  try {
    const data = await callExternalAPI(endpoint);
    return object(data);
  } catch (err) {
    // Use error() helper to signal failure to the client
    return error(
      `Unable to fetch data from ${endpoint}.\n` +
      `Error: ${err.message}\n` +
      `Please check the endpoint and try again.`
    );
  }
})
```

```typescript Expanded
server.tool({
  name: 'external_api',
  description: 'Call external API',
  schema: z.object({
    endpoint: z.string().url().describe('The API endpoint URL')
  })
}, async ({ endpoint }) => {
  try {
    const data = await callExternalAPI(endpoint);
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(data, null, 2)
      }],
      structuredContent: data,
      _meta: {
        mimeType: 'application/json'
      }
    }
  } catch (err) {
    // Return error response with isError flag set
    return {
      isError: true,
      content: [{
        type: 'text',
        text: `Unable to fetch data from ${endpoint}.\n` +
              `Error: ${err.message}\n` +
              `Please check the endpoint and try again.`
      }],
      _meta: {
        mimeType: 'text/plain'
      }
    }
  }
})
```

</CodeGroup>

## Logging from Tools

Tools can send log messages to clients during execution using `ctx.log()`. This is useful for reporting progress, debugging tool behavior, and providing real-time feedback during long-running operations.

```typescript
server.tool({
  name: 'process_files',
  description: 'Process multiple files',
  schema: z.object({
    files: z.array(z.string())
  })
}, async ({ files }, ctx) => {
  await ctx.log('info', 'Starting file processing');
  
  for (const file of files) {
    await ctx.log('debug', `Processing ${file}`);
    // ... process file ...
  }
  
  await ctx.log('info', 'Processing completed');
  return text(`Processed ${files.length} files`);
})
```

The `ctx.log()` function accepts a log level (`'debug'`, `'info'`, `'notice'`, `'warning'`, `'error'`, `'critical'`, `'alert'`, `'emergency'`), a message string, and an optional logger name. See [Server Logging](./logging#tool-logging-with-ctxlog) for complete documentation on log levels and best practices.


## Testing


Use the built-in inspector for interactive testing:

1. Start your server with the inspector
2. Navigate to `http://localhost:3000/inspector`
3. Select your tool from the list
4. Enter parameter values
5. Execute and view results

## Next Steps

- [Response Helpers](./response-helpers) - Utility functions for creating responses
- [Resources Guide](./resources) - Managing static and dynamic content
- [UI Widgets](./ui-widgets) - Creating interactive UI components
- [Examples](https://github.com/mcp-use/mcp-use/tree/main/examples) - Real-world tool implementations